using System.Data;
using System.Data.Common;
using System.Diagnostics;

namespace Dapper.Extensions.Hooks;

internal class WrappedCommand<T>(
	DbCommand command,
	DbConnection connection,
	ISqlHooks<T> hooks,
	T context)
	: DbCommand
{
	private DbConnection _connection = connection;

	protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
	{
		var sw = Stopwatch.StartNew();
		try
		{
			return command.ExecuteReader(behavior);
		}
		finally
		{
			hooks.CommandExecuted(this, context, sw.ElapsedMilliseconds);
		}
	}

	protected override async Task<DbDataReader> ExecuteDbDataReaderAsync(
		CommandBehavior behavior, CancellationToken cancellationToken)
	{
		var sw = Stopwatch.StartNew();
		try
		{
			return await command.ExecuteReaderAsync(behavior, cancellationToken);
		}
		finally
		{
			hooks.CommandExecuted(this, context, sw.ElapsedMilliseconds);
		}
	}

	public override int ExecuteNonQuery()
	{
		var sw = Stopwatch.StartNew();
		try
		{
			return command.ExecuteNonQuery();
		}
		finally
		{
			hooks.CommandExecuted(this, context, sw.ElapsedMilliseconds);
		}
	}

	public override async Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken)
	{
		var sw = Stopwatch.StartNew();
		try
		{
			return await command.ExecuteNonQueryAsync(cancellationToken);
		}
		finally
		{
			hooks.CommandExecuted(this, context, sw.ElapsedMilliseconds);
		}
	}

	public override object ExecuteScalar()
	{
		var sw = Stopwatch.StartNew();
		try
		{
			return command.ExecuteScalar();
		}
		finally
		{
			hooks.CommandExecuted(this, context, sw.ElapsedMilliseconds);
		}
	}

	public override async Task<object> ExecuteScalarAsync(CancellationToken cancellationToken)
	{
		var sw = Stopwatch.StartNew();
		try
		{
			return await command.ExecuteScalarAsync(cancellationToken);
		}
		finally
		{
			hooks.CommandExecuted(this, context, sw.ElapsedMilliseconds);
		}
	}

	//other members

	public override string CommandText
	{
		get => command.CommandText;
		set => command.CommandText = value;
	}

	public override int CommandTimeout
	{
		get => command.CommandTimeout;
		set => command.CommandTimeout = value;
	}

	public override CommandType CommandType
	{
		get => command.CommandType;
		set => command.CommandType = value;
	}

	protected override DbConnection DbConnection
	{
		get => _connection;
		set => _connection = value;
	}

	protected override DbTransaction DbTransaction
	{
		get => command.Transaction;
		set => command.Transaction = value;
	}

	public override bool DesignTimeVisible
	{
		get => command.DesignTimeVisible;
		set => command.DesignTimeVisible = value;
	}

	public override UpdateRowSource UpdatedRowSource
	{
		get => command.UpdatedRowSource;
		set => command.UpdatedRowSource = value;
	}

	protected override DbParameterCollection DbParameterCollection => command.Parameters;
	public override void Cancel() => command.Cancel();
	public override void Prepare() => command.Prepare();
	protected override DbParameter CreateDbParameter() => command.CreateParameter();

	protected override void Dispose(bool disposing)
	{
		if (disposing)
			command?.Dispose();

		base.Dispose(disposing);
	}
}
